﻿namespace SenenFernandez.SmartExtensionMethods
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    /// <summary>
    /// Extension Methods for Data Type Conversion.
    /// </summary>
    public static class SmartConvert
    {
        /// <summary>
        /// Checks if the object is not null.
        /// </summary>
        public static bool IsNotNull(this object value)
        {
            return value != null && value != DBNull.Value;
        }

        /// <summary>
        /// Converts the string representation of a number to its double-precision nullable floating-point number equivalent.
        /// </summary>
        public static int? ToNullableInt<T>(this T value)
        {
            return value.IsNotNull() ? (int?) int.Parse(value.ToString()) : default(int?);
        }

        /// <summary>
        /// Converts the string representation of a number to its double-precision nullable floating-point number equivalent.
        /// </summary>
        public static double? ToNullableDouble<T>(this T value)
        {
            return value.IsNotNull() ? (double?) double.Parse(value.ToString()) : default(double?);
        }

        /// <summary>
        /// Converts the string representation of a number to its 32-bit signed integer equivalent.
        /// </summary>
        public static int ToInt<T>(this T value)
        {
            return int.Parse(value.ToString());
        }

        /// <summary>
        /// Converts the string representation of a number to its 32-bit signed integer equivalent.
        /// </summary>
        public static int ToInt<T>(this T value, int defaultValue)
        {
            int result = defaultValue;
            if (value.IsNotNull())
            {
                int.TryParse(value.ToString(), out result);
            }
            return result;
        }

        /// <summary>
        /// Converts the specified string representation of a logical value to its Boolean equivalent.
        /// </summary>
        public static bool ToBool<T>(this T value)
        {
            return Convert.ToBoolean(value);
        }

        /// <summary>
        /// Converts the specified string representation of a logical value to its Boolean equivalent.
        /// </summary>
        public static bool ToBool<T>(this T value, bool defaultValue)
        {
            bool result = defaultValue;
            if (value.IsNotNull())
            {
                bool.TryParse(value.ToString(), out result);
            }
            return result;
        }

        /// <summary>
        /// Converts the string representation of a number to its double equivalent.
        /// </summary>
        public static double ToDouble<T>(this T value)
        {
            return double.Parse(value.ToString());
        }

        /// <summary>
        /// Converts the string representation of a number to its double equivalent.
        /// </summary>
        public static double ToDouble<T>(this T value, double defaultValue)
        {
            double result = defaultValue;
            if (value.IsNotNull())
            {
                double.TryParse(value.ToString(), out result);
            }
            return result;
        }

        /// <summary>
        /// Enum from string object.
        /// </summary>
        public static T ToEnum<T>(this T value)
        {
            return (T)System.Enum.Parse(typeof(T), value.ToString());
        }

        /// <summary>
        /// Converts the shift operator of a number to its 32-bit signed integer equivalent.
        /// </summary>
        public static int ToInt(this short high, short low)
        {
            return (high << 16) | (low & 0xffff);
        }

        /// <summary>
        /// Converts the shift operator of a number to its 64-bit signed integer equivalent.
        /// </summary>
        public static long ToLong(this short highhigh, short high, short low, short lowlow)
        {
            return ((highhigh << 48) | (high << 32) | (low << 16) | (lowlow & 0xffff));
        }

        /// <summary>
        /// Converts a specified value to an 8-bit signed short.
        /// </summary>
        public static short ToByte(this short value, int offset)
        {
            return Convert.ToInt16(Convert.ToByte(BitConverter.GetBytes(value)[offset]));
        }

        /// <summary>
        /// Converts a specified value to a bit signed integer.
        /// </summary>
        public static int ToBit(this short value, int offset)
        {
            return 1 & (Convert.ToByte(value) >> offset);
        }

        /// <summary>
        /// To convert a object to a byte array.
        /// </summary>
        public static byte[] ToByte(this object value)
        {
            return value is byte[] ? (byte[]) value : default(byte[]);
        }

        /// <summary>
        /// Converts the value of the current DateTime object to its equivalent string representation using the specified format.
        /// </summary>
        public static string ToStringDateTimeOffset(this object value)
        {
            const string format = "yyyy/MM/dd HH:mm:ss.fffffff zzz";
            return value is DateTimeOffset
                       ? ((DateTimeOffset)value).ToString(format)
                       : ((DateTime)value).ToString(format);
        }

        /// <summary>
        /// Converts the specified object representation of a date, time, and offset to its DateTimeOffset equivalent.
        /// </summary>
        public static DateTimeOffset ToDateTimeOffset<T>(this T value)
        {
            return DateTimeOffset.Parse(value.ToStringDateTimeOffset());
        }

        /// <summary>
        /// Converts the specified object representation of a date, time, and offset to its DateTimeOffset equivalent.
        /// </summary>
        public static DateTimeOffset? ToNullableDateTimeOffset<T>(this T value)
        {
            return value.IsNotNull() ? value as DateTimeOffset? : default(DateTimeOffset?);
        }

        /// <summary>
        /// Converts the specified object representation of a date, time, and offset to its DateTimeOffset equivalent.
        /// </summary>
        public static DateTimeOffset ToDateTimeOffset(this byte[] value, int startIndex)
        {
            long dateTime = BitConverter.ToInt64(value, startIndex);
            long offset = BitConverter.ToInt64(value, startIndex + 8);
            return new DateTimeOffset(new DateTime(dateTime), new TimeSpan(offset));
        }

        /// <summary>
        /// Returns a ToByteTime compatible object to Tda.
        /// </summary>
        public static byte[] ToByteTime(this DateTimeOffset value)
        {
            return BitConverter.GetBytes(value.Ticks).Combine(BitConverter.GetBytes(value.Offset.Ticks));
        }

        /// <summary>
        /// Creates a delimited string from an ArrayList.
        /// </summary>
        public static string ToDelimitedString<T>(this IEnumerable<T> list, string separator = ";")
        {
            return list != null ? string.Join(separator, list) : default(string);
        }

        /// <summary>
        /// Combine two byte arrays.
        /// </summary>
        public static byte[] Combine(this byte[] value, byte[] newValue)
        {
            var result = new byte[value.Length + newValue.Length];
            Buffer.BlockCopy(value, 0, result, 0, value.Length);
            Buffer.BlockCopy(newValue, 0, result, value.Length, newValue.Length);
            return result;
        }

        /// <summary>
        /// Returns a copy of this string.
        /// </summary>
        public static string GetValueOrEmpty(this string value)
        {
            return string.IsNullOrEmpty(value) ? string.Empty : value;
        }

        /// <summary>
        /// Returns a copy of this string.
        /// </summary>
        public static string GetValueOrDefault<T>(this T value, string defaultValue)
        {
            var result = IsNotNull(value) ? value.ToString() : default(string);
            return !string.IsNullOrEmpty(result) ? result : defaultValue;
        }

        /// <summary>
        /// Converts string separated list to an ArrayList.
        /// </summary>
        public static string[] ToArray(this string value, char separator = ';')
        {
            return value.GetValueOrEmpty()
                .Split(new char[] {separator}, StringSplitOptions.RemoveEmptyEntries);
        }

        /// <summary>
        /// Converts a delimited string to List<double>
        /// </summary>
        public static List<double> ToDoubleList(this string value, char separator = ';', int max = 0)
        {
            var result = value.GetValueOrEmpty()
                .Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries)
                .Select(source => double.Parse(source, CultureInfo.InvariantCulture));
            if (max > 0)
            {
                result = result.Take(max);
            }
            return result.ToList();
        }

        /// <summary>
        /// Returns a copy of this string object converted to uppercase using the casing rules of the invariant culture.
        /// </summary>
        public static string ToUpperOrEmpty(this string value)
        {
            return value.GetValueOrEmpty().ToUpperInvariant();
        }

        /// <summary>
        /// Returns a copy of this string object converted to lowercase using the casing rules of the invariant culture.
        /// </summary>
        public static string ToLowerOrEmpty(this string value)
        {
            return value.GetValueOrEmpty().ToLowerInvariant();
        }
    }
}